#ifndef __CSOCKET_H__
#define __CSOCKET_H__
#include <list>
#include <vector>
#include <sys/epoll.h>
#include <sys/socket.h>

#include "packet.h"
#include "spinlock.h"

const int MAX_EVENTS = 512;
const int LISTEN_BACKLOG = 511;

class CSocket; //前向声明
typedef struct SListenPort* pSListen;
typedef struct SConnection* pSConnection;
typedef void (CSocket::*eventHandle)(pSConnection c); //CSocket成员函数指针
//using eventHandle = void (CSocket::*)(pSConnection c);

/**
 * @brief 用来描述套接字监听端口相关的结构体
 *
 */
struct SListenPort
{
    int             port; //端口号
    int             fd; //套接字文件描述符
    pSConnection    connection; //指向连接池中的一个连接的指针
    SListenPort(): SListenPort(0, 0) {} //委托有参构造
    SListenPort(int p, int f): port(p), fd(f), connection(nullptr) {} //传入端口和sockfd
};

/**
 * @brief 用来描述一个TCP连接的结构体
 *
 */
struct SConnection
{
    int             fd; //套接字文件描述符
    pSListen        listening; //如果这个连接被分配给了一个监听套接字，那么这个指针就会指向套接字对应的结构体
    unsigned        instance:1; //位域，表示是否失效
    uint64_t        iCurSeq; //序列号，用于检测错包废包
    uint8_t         wrRdy;  //写就绪标记
    struct sockaddr s_sockaddr; //linux系统定义的地址结构体
    eventHandle     rdHandle; //可读事件回调函数
    eventHandle     wrHandle; //可写事件回调函数
    char            curStat; //当前收包的状态
    char            dataHeadInfo[DATA_BUFSIZE]; //保存数据包的包头
    char*           pRecvBuf; //接受缓冲区的头指针
    uint32_t        recvLen; //配合pRecvBuf使用
    bool            ifNew; //判断是否new申请过内存
    char*           pNewMem; //new出来的内存空间指针
    pSConnection    next; //链表中的下一个对象地址
};

/**
 * @brief 消息头，在收到数据包时，用来记录一些额外信息
 */
typedef struct __MSG_HEADER {
    pSConnection    pConn; //指向消息对应的连接的指针
    uint64_t        curSeq; //记录收到数据包时对应连接的seq序列号
    //待扩展...
} MSG_HEADER, *pMSG_HEADER;

class CSocket
{
public:
    CSocket();
    virtual ~CSocket();
    virtual bool Initialize();

public:
    /**
     * @brief 从out消息队列中取出一个消息
     *
     */
    char* outMsgRecvQueue();

    /**
     * @brief 处理客户端请求，先声明，以后用到再定义
     *
     * @param pMsgBuf 客户端请求的内容
     */
    virtual void threadRecvProcFunc(char* pMsgBuf);

    /**
     * @brief 初始化epoll功能
     *
     * @return int 返回0表示成功
     */
    int epollInit();

    /**
     * @brief 增加epoll事件
     *
     * @param sockfd 套接字fd
     * @param rdevt 读事件标志
     * @param wrevt 写事件标志
     * @param oflag 其他事件标志
     * @param etype 事件类型
     * @param c 连接结构体指针
     * @return int 成功返回1，失败返回0
     */
    int epollAddEvent(int sockfd, int rdevt, int wrevt, uint32_t oflag,
                      uint32_t etype, pSConnection c);

    /**
     * @brief epoll等待接收和处理事件
     *
     * @param timer 超时时限
     * @return int 正常返回1，异常返回0但是也要继续执行
     */
    int epollProcessEvents(int timer);

private:
    /**
     * @brief 读取配置相关操作
     *
     */
    void ReadConf();

    /**
     * @brief 监听配置中设置的多个端口
     *
     * @return true 成功
     * @return false 失败
     */
    bool openListeningSockets();

    /**
     * @brief 关闭所有监听套接字
     *
     */
    void closeListeningSockets();

    /**
     * @brief Set the Non Blocking object
     *
     * @param sockfd 套接字fd
     * @return true 成功
     * @return false 失败
     */
    bool setNonBlocking(int sockfd);

    /**
     * @brief 建立新连接
     *
     * @param oldc 传入一个指向空闲连接的指针
     */
    void eventAccept(pSConnection oldc);

    /**
     * @brief 数据包到达时的处理函数
     *
     * @param c 连接指针
     */
    void waitReqHandle(pSConnection c);

    /**
     * @brief 数据包头完整接收后的处理函数，包处理阶段，称为P1
     *
     * @param c 连接指针
     */
    void waitReqHandleProcP1(pSConnection c);

    /**
     * @brief 完整数据包收到后的处理函数，称为P2
     *
     * @param c 连接指针
     */
    void waitReqHandleProcP2(pSConnection c);

    /**
     * @brief 收到完整消息后，入消息队列
     *
     * @param buf 待入队的消息
     * @param irmqc 消息队列中的数量
     */
    void inMsgRecvQueue(char* buf, int& irmqc);

    /**
     * @brief 清理消息接收队列
     *
     */
    void clearMsgRecvQueue();

    /**
     * @brief 获取通信对端信息，根据sa的信息获取地址端口字符串
     *
     * @param sa linux系统定义地址结构体
     * @param port 端口号
     * @param text
     * @param len
     * @return size_t 返回字符串长度
     */
    size_t sock_ntop(struct sockaddr* sa, int port, char* text, size_t len);

    /**
     * @brief 接收从客户端传来数据的函数
     *
     * @param c 连接指针
     * @param buf 接收缓冲区指针
     * @param len 期望收到多少字节的数据
     * @return ssize_t 返回接收到的数据长度，-1表示发生异常
     */
    ssize_t recvProcess(pSConnection c, char* buf, ssize_t len);

    /**
     * @brief Get the Connection object
     *
     * @param sockfd 套接字fd
     * @return pSConnection 返回一个空闲连接指针
     */
    pSConnection getConnection(int sockfd);

    /**
     * @brief 将c指向的连接释放，放回空闲连接链表的头部
     *
     * @param c 待释放的连接
     */
    void releaseConnection(pSConnection c);

    /**
     * @brief 关闭连接套接字，释放连接资源
     * @param  c 待关闭的连接
     */
    void closeConnection(pSConnection c);

private:
    int                 m_maxWorkerNum; //最大连接数
    int                 m_listenPortNum; //监听的端口数量
    int                 m_epollfd; //epoll句柄
    pSConnection        m_pConnections; //连接池首地址
    pSConnection        m_pHeadFreeConnections; //空闲连接链表的头地址
    int                 m_curConnectionNum; //当前连接数
    int                 m_freeConnectionNum; //当前空闲连接数
    std::vector<pSListen> m_listenSocketList; //监听套接字队列
    struct epoll_event  m_events[MAX_EVENTS]; //epoll事件数组，存放epoll_wait()返回的事件
    size_t              m_pktHeaderLen;
    size_t              m_msgHeaderLen;
    std::list<char *>   m_MsgRecvQueue; //接收消息队列
    int                 m_recvMsgQueueSize;
    pthread_mutex_t     m_recvMsgQueueMutex; //接收消息队列互斥量
    CSpinLock           m_mqSpinLock; //也可以使用自旋锁
};
#endif // __CSOCKET_H__